using System;
using System.Collections.Generic;
using System.Linq;
using Headsight.BPMN2WF.BPMN.Schemas;
using Headsight.BPMN2WF.Utils;

namespace Headsight.BPMN2WF.Testing.TestObjCtors
{
    /// <summary>
    /// <see cref="tProcess"/> <see cref="ITestObjCtor{T}"/>
    /// </summary>
    public class tProcessCtor : ItProcessCtor
    {
        private String _id;
        private String _name;
        private readonly IList<Func<tFlowElement>> _tFlowElements;
        private readonly IDictionary<String, tFlowElement> _tFlowElems;


        public tProcessCtor()
        {
            _tFlowElements = new List<Func<tFlowElement>>();
            _tFlowElems = new Dictionary<String, tFlowElement>();
        }

        public tProcess Create()
        {
            var tFlowElements = _tFlowElements.Select(x => x()).ToList();
            tFlowElements.AddRange(_tFlowElems.Values);

            var result = new tProcess
                             {
                                 id = _id ?? "tProcessId", 
                                 name = _name ?? "tProcessName", 
                                 Items = tFlowElements.ToArray()
                             };

            return result;
        }

        public tProcessCtor WithId(String id)
        {
            _id = id;

            return this;
        }

        public tProcessCtor WithName(String name)
        {
            _name = name;

            return this;
        }

        public tProcessCtor With(params tFlowElement[] tFlowElements)
        {
            foreach (var tFlowElement in tFlowElements)
            {
                var t = tFlowElement;
                _tFlowElements.Add(() => t);
            }

            return this;
        }

        public tProcessCtor With<T>(ITestObjCtor<T> tFlowElementTestObjCtor) where T : tFlowElement
        {
            _tFlowElements.Add(tFlowElementTestObjCtor.Create);

            return this;
        }

        public tProcessCtor Flow(String id1, String id2)
        {
            if (!_tFlowElems.ContainsKey(id1))
            {
                var tFlowNode = new tFlowNodeCtor().WithId(id1).Create();
                tFlowNode.name = "Name: " + id1;
                _tFlowElems[id1] = tFlowNode;
            }

            if (!_tFlowElems.ContainsKey(id2))
            {
                var tFlowNode = new tFlowNodeCtor().WithId(id2).Create();
                tFlowNode.name = "Name: " + id2;
                _tFlowElems[id2] = tFlowNode;
            }

            var tSeqlFlowId = String.Format("{0}_{1}", id1, id2);
            if (!_tFlowElems.ContainsKey(tSeqlFlowId))
            {
                var tSeqenceFlow = new tSequenceFlowCtor().WithId(tSeqlFlowId).Create();

                tSeqenceFlow.sourceRef = id1;
                tSeqenceFlow.targetRef = id2;

                _tFlowElems[tSeqlFlowId] = tSeqenceFlow;
            }

            return this;
        }

        public tProcessCtor Branch(string id1, string id2)
        {
            if (!_tFlowElems.ContainsKey(id1) || !(_tFlowElems[id1] is tExclusiveGateway))
            {
                if (_tFlowElems.ContainsKey(id1))
                {
                    _tFlowElems.Remove(id1);
                }

                _tFlowElems[id1] = new tExclusiveGateway()
                                       {
                                           id = id1
                                       };
            }

            if (!_tFlowElems.ContainsKey(id2))
            {
                var tFlowNode = new tFlowNodeCtor().WithId(id2).Create();
                tFlowNode.name = "Name: " + id2;
                _tFlowElems[id2] = tFlowNode;
            }

            var tSeqlFlowId = String.Format("{0}_{1}", id1, id2);
            if (!_tFlowElems.ContainsKey(tSeqlFlowId))
            {
                var tSeqenceFlow = new tSequenceFlowCtor().WithId(tSeqlFlowId).Create();

                tSeqenceFlow.sourceRef = id1;
                tSeqenceFlow.targetRef = id2;
                tSeqenceFlow.conditionExpression = new tExpression
                                                       {
                                                           Text = new[] {"True = False"}
                                                       };

                _tFlowElems[tSeqlFlowId] = tSeqenceFlow;
            }

            return this;
        }

        public tProcessCtor WithStartEvent(String startEvent)
        {
            _tFlowElems[startEvent] = new tStartEvent()
            {
                id = startEvent, name = startEvent
            };

            return this;
        }
    }
}